package client

import (
	
	
	
	
	
)

// TGSREQGenerateAndExchange generates the TGS_REQ and performs a TGS exchange to retrieve a ticket to the specified SPN.
func ( *Client) ( types.PrincipalName,  string,  messages.Ticket,  types.EncryptionKey,  bool) ( messages.TGSReq,  messages.TGSRep,  error) {
	,  = messages.NewTGSReq(.Credentials.CName(), , .Config, , , , )
	if  != nil {
		return , , krberror.Errorf(, krberror.KRBMsgError, "TGS Exchange Error: failed to generate a new TGS_REQ")
	}
	return .TGSExchange(, , .Ticket, , 0)
}

// TGSExchange exchanges the provided TGS_REQ with the KDC to retrieve a TGS_REP.
// Referrals are automatically handled.
// The client's cache is updated with the ticket received.
func ( *Client) ( messages.TGSReq,  string,  messages.Ticket,  types.EncryptionKey,  int) (messages.TGSReq, messages.TGSRep, error) {
	var  messages.TGSRep
	,  := .Marshal()
	if  != nil {
		return , , krberror.Errorf(, krberror.EncodingError, "TGS Exchange Error: failed to marshal TGS_REQ")
	}
	,  := .sendToKDC(, )
	if  != nil {
		if ,  := .(messages.KRBError);  {
			return , , krberror.Errorf(, krberror.KDCError, "TGS Exchange Error: kerberos error response from KDC when requesting for %s", .ReqBody.SName.PrincipalNameString())
		}
		return , , krberror.Errorf(, krberror.NetworkingError, "TGS Exchange Error: issue sending TGS_REQ to KDC")
	}
	 = .Unmarshal()
	if  != nil {
		return , , krberror.Errorf(, krberror.EncodingError, "TGS Exchange Error: failed to process the TGS_REP")
	}
	 = .DecryptEncPart()
	if  != nil {
		return , , krberror.Errorf(, krberror.EncodingError, "TGS Exchange Error: failed to process the TGS_REP")
	}
	if ,  := .Verify(.Config, ); ! {
		return , , krberror.Errorf(, krberror.EncodingError, "TGS Exchange Error: TGS_REP is not valid")
	}

	if .Ticket.SName.NameString[0] == "krbtgt" && !.Ticket.SName.Equal(.ReqBody.SName) {
		if  > 5 {
			return , , krberror.Errorf(, krberror.KRBMsgError, "TGS Exchange Error: maximum number of referrals exceeded")
		}
		// Server referral https://tools.ietf.org/html/rfc6806.html#section-8
		// The TGS Rep contains a TGT for another domain as the service resides in that domain.
		.addSession(.Ticket, .DecryptedEncPart)
		 := .Ticket.SName.NameString[len(.Ticket.SName.NameString)-1]
		++
		if types.IsFlagSet(&.ReqBody.KDCOptions, flags.EncTktInSkey) && len(.ReqBody.AdditionalTickets) > 0 {
			,  = messages.NewUser2UserTGSReq(.Credentials.CName(), , .Config, , , .ReqBody.SName, .Renewal, .ReqBody.AdditionalTickets[0])
			if  != nil {
				return , , 
			}
		}
		,  = messages.NewTGSReq(.Credentials.CName(), , .Config, .Ticket, .DecryptedEncPart.Key, .ReqBody.SName, .Renewal)
		if  != nil {
			return , , 
		}
		return .(, , .Ticket, .DecryptedEncPart.Key, )
	}
	.cache.addEntry(
		.Ticket,
		.DecryptedEncPart.AuthTime,
		.DecryptedEncPart.StartTime,
		.DecryptedEncPart.EndTime,
		.DecryptedEncPart.RenewTill,
		.DecryptedEncPart.Key,
	)
	.Log("ticket added to cache for %s (EndTime: %v)", .Ticket.SName.PrincipalNameString(), .DecryptedEncPart.EndTime)
	return , , 
}

// GetServiceTicket makes a request to get a service ticket for the SPN specified
// SPN format: <SERVICE>/<FQDN> Eg. HTTP/www.example.com
// The ticket will be added to the client's ticket cache
func ( *Client) ( string) (messages.Ticket, types.EncryptionKey, error) {
	var  messages.Ticket
	var  types.EncryptionKey
	if , ,  := .GetCachedTicket();  {
		// Already a valid ticket in the cache
		return , , nil
	}
	 := types.NewPrincipalName(nametype.KRB_NT_PRINCIPAL, )
	 := .spnRealm()

	// if we don't know the SPN's realm, ask the client realm's KDC
	if  == "" {
		 = .Credentials.Realm()
	}

	, ,  := .sessionTGT()
	if  != nil {
		return , , 
	}
	, ,  := .TGSREQGenerateAndExchange(, , , , false)
	if  != nil {
		return , , 
	}
	return .Ticket, .DecryptedEncPart.Key, nil
}